{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZloPIuRHn97X"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors. [Licensed under the Apache License, Version 2.0](#scrollTo=Afd8bu4xJOgh)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tNgCmfUvJNoF"
      },
      "outputs": [],
      "source": [
        "// #@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
        "// Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "// you may not use this file except in compliance with the License.\n",
        "// You may obtain a copy of the License at\n",
        "//\n",
        "// https://www.apache.org/licenses/LICENSE-2.0\n",
        "//\n",
        "// Unless required by applicable law or agreed to in writing, software\n",
        "// distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "// See the License for the specific language governing permissions and\n",
        "// limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AlvdCHw5JGyx"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/swift/tutorials/protocol_oriented_generics\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/swift/tutorials/protocol_oriented_generics.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/swift/tutorials/protocol_oriented_generics.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c_1u7JSBMx3x"
      },
      "source": [
        "# プロトコル指向プログラミングとジェネリック\n",
        "\n",
        "このチュートリアルでは、プロトコル指向プログラミングを説明し、日常的な設定におけるジェネックとの使用方法をさまざまな例を通じて紹介します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LP0gMw56TlvH"
      },
      "source": [
        "## プロトコル\n",
        "\n",
        "プログラミング言語において、継承は、強力なコードの編成方法であり、プログラムの複数のコンポーネント間でのコードの共有を実現することができます。\n",
        "\n",
        "Swift では、さまざまな方法で継承を表現できます。クラスの継承など、すでにいくつかの方法はほかの言語からご存じかもしれませんが、Swift ではプロトコルも継承可能です。\n",
        "\n",
        "このチュートリアルでは、さまざまなトレードオフを通じて似たような目標を達成できる、サブクラス化の代替手法としてのプロトコルについて説明します。Swift では、プロトコルには複数の抽象メンバーが含まれます。クラス、構造、および列挙は複数のプロトコルに適合し、遡及的に適合関係を確立することができます。Swift においてサブクラス化では簡単に表現できない設計も、こういったプロトコルの仕様によって可能となります。プロトコルの使用をサポートするイディオム（拡張機能とプロトコルの制約）のほか、プロトコルの制限についても説明しています。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5AIIH5Q59b41"
      },
      "source": [
        "## Swift 💖 の値の型について\n",
        "\n",
        "参照セマンティクスのあるクラスのほか、Swift は値として渡される列挙や構造をサポートしています。列挙と構造はクラスが提供する多数の機能をサポートしています。では、それらを見てみましょう！\n",
        "\n",
        "まず、列挙とクラスがどれくらい似ているのかを確認しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zCN0Uc0w-gng"
      },
      "outputs": [],
      "source": [
        "enum Color: String {\n",
        "    case red = \"red\"\n",
        "    case green = \"green\"\n",
        "    case blue = \"blue\"\n",
        "    // A computed property. Note that enums cannot contain stored properties.\n",
        "    var hint: String {\n",
        "        switch self {\n",
        "            case .red:\n",
        "                return \"Roses are this color.\"\n",
        "            case .green:\n",
        "                return \"Grass is this color.\"\n",
        "            case .blue:\n",
        "                return \"The ocean is this color.\"\n",
        "        }\n",
        "    }\n",
        "    \n",
        "    // An initializer like for classes.\n",
        "    init?(color: String) {\n",
        "        switch color {\n",
        "        case \"red\":\n",
        "            self = .red\n",
        "        case \"green\":\n",
        "            self = .green\n",
        "        case \"blue\":\n",
        "            self = .blue\n",
        "        default:\n",
        "            return nil\n",
        "        }\n",
        "    }\n",
        "}\n",
        "\n",
        "// Can extend the enum as well!\n",
        "extension Color {\n",
        "    // A function.\n",
        "    func hintFunc() -> String {\n",
        "        return self.hint\n",
        "    }\n",
        "}\n",
        "\n",
        "let c = Color.red\n",
        "print(\"Give me a hint for c: \\(c.hintFunc())\")\n",
        "\n",
        "let invalidColor = Color(color: \"orange\")\n",
        "print(\"is invalidColor nil: \\(invalidColor == nil)\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ueiGy9gCgypk"
      },
      "source": [
        "次に、構造を確認しましょう。構造を継承することはできない代わりにプロトコルを使用するところに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ns4qCE1pg3uJ"
      },
      "outputs": [],
      "source": [
        "struct FastCar {\n",
        "    // Can have variables and constants as stored properties.\n",
        "    var color: Color\n",
        "    let horsePower: Int\n",
        "    // Can have computed properties.\n",
        "    var watts: Float {\n",
        "       return Float(horsePower) * 745.7\n",
        "    }\n",
        "    // Can have lazy variables like in classes!\n",
        "    lazy var titleCaseColorString: String = {\n",
        "        let colorString = color.rawValue\n",
        "        return colorString.prefix(1).uppercased() + \n",
        "               colorString.lowercased().dropFirst()\n",
        "    }()\n",
        "    // A function.\n",
        "    func description() -> String {\n",
        "        return \"This is a \\(color) car with \\(horsePower) horse power!\"\n",
        "    }\n",
        "    // Can create a variety of initializers.\n",
        "    init(color: Color, horsePower: Int) {\n",
        "        self.color = color\n",
        "        self.horsePower = horsePower\n",
        "    }\n",
        "    // Can define extra initializers other than the default one.\n",
        "    init?(color: String, horsePower: Int) {\n",
        "        guard let enumColor = Color(color: color) else {\n",
        "            return nil\n",
        "        }\n",
        "        self.color = enumColor\n",
        "        self.horsePower = horsePower\n",
        "    }\n",
        "}\n",
        "\n",
        "var car = FastCar(color: .red, horsePower: 250)\n",
        "print(car.description())\n",
        "print(\"Horse power in watts: \\(car.watts)\")\n",
        "print(car.titleCaseColorString)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1Hw8bpQIlaWT"
      },
      "source": [
        "最後に、列挙と構造が、クラスとは異なり、値の型として渡される様子を見てみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "C67qzGBVlhdo"
      },
      "outputs": [],
      "source": [
        "// Notice we have no problem modifying a constant class with \n",
        "// variable properties.\n",
        "class A {\n",
        "  var a = \"a\"\n",
        "}\n",
        "\n",
        "func foo(_ a: A) {\n",
        "  a.a = \"foo\"\n",
        "}\n",
        "let a = A()\n",
        "print(a.a)\n",
        "foo(a)\n",
        "print(a.a)\n",
        "\n",
        "/* \n",
        "Uncomment the following code to see how an error is thrown.\n",
        "Structs are implicitly passed by value, so we cannot modify it.\n",
        "> \"error: cannot assign to property: 'car' is a 'let' constant\"\n",
        "*/\n",
        "\n",
        "// func modify(car: FastCar, toColor color: Color) -> Void {\n",
        "//   car.color = color\n",
        "// }\n",
        "\n",
        "// car = FastCar(color: .red, horsePower: 250)\n",
        "// print(car.description())\n",
        "// modify(car: &car, toColor: .blue)\n",
        "// print(car.description())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0BNxC5RyoKM7"
      },
      "source": [
        "## プロトコルを使いましょう\n",
        "\n",
        "様々な車種のプロトコルを作成することから始めます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3ZSm1uTWoJ0h"
      },
      "outputs": [],
      "source": [
        "protocol Car {\n",
        "    var color: Color { get set }\n",
        "    var price: Int { get }\n",
        "    func turnOn()\n",
        "    mutating func drive()\n",
        "}\n",
        "\n",
        "protocol Electric {\n",
        "    mutating func recharge()\n",
        "    // percentage of the battery level, 0-100%.\n",
        "    var batteryLevel: Int { get set }\n",
        "}\n",
        "\n",
        "protocol Gas {\n",
        "    mutating func refill()\n",
        "    // # of liters the car is holding, varies b/w models.\n",
        "    var gasLevelLiters: Int { get set }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aV_F6MyLps3h"
      },
      "source": [
        "オブジェクト指向の（複数の継承を行えない）世界では、`Electric` と `Gas` という抽象クラスを作成してから、クラス継承を使用して両方を `Car` の継承とし、さらに特定の車種をベースクラスとしていたかもしれません。しかし、ここでは、これらはカップリングが**全くない**完全に独立したプロトコルになっています。このため、システム全体の設計方法にさらなる柔軟性が付加されています。\n",
        "\n",
        "Tesla を定義してみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LkGRtwz3psrP"
      },
      "outputs": [],
      "source": [
        "struct TeslaModelS: Car, Electric {\n",
        "    var color: Color // Needs to be a var since `Car` has a getter and setter.\n",
        "    let price: Int\n",
        "    var batteryLevel: Int\n",
        "    \n",
        "    func turnOn() {\n",
        "        print(\"Starting all systems!\")\n",
        "    }\n",
        "\n",
        "    mutating func drive() {\n",
        "        print(\"Self driving engaged!\")\n",
        "        batteryLevel -= 8\n",
        "    }\n",
        "\n",
        "    mutating func recharge() {\n",
        "        print(\"Recharging the battery...\")\n",
        "        batteryLevel = 100\n",
        "    }\n",
        "}\n",
        "\n",
        "var tesla = TeslaModelS(color: .red, price: 110000, batteryLevel: 100)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K5KB0IGLrkcm"
      },
      "source": [
        "これは、`Car` と `Electric` の両方のプロトコルに適合する新しい構造 `TeslaModelS` を指定しています。\n",
        "\n",
        "では、ガソリン車を定義しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GPLKFUAOrMp-"
      },
      "outputs": [],
      "source": [
        "struct Mustang: Car, Gas{\n",
        "    var color: Color\n",
        "    let price: Int\n",
        "    var gasLevelLiters: Int\n",
        "    \n",
        "    func turnOn() {\n",
        "        print(\"Starting all systems!\")\n",
        "    }\n",
        "    \n",
        "    mutating func drive() {\n",
        "        print(\"Time to drive!\")\n",
        "        gasLevelLiters -= 1\n",
        "    }\n",
        "    \n",
        "    mutating func refill() {\n",
        "        print(\"Filling the tank...\")\n",
        "        gasLevelLiters = 25\n",
        "    }\n",
        "}\n",
        "\n",
        "var mustang = Mustang(color: .red, price: 30000, gasLevelLiters: 25)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jrbCRkglsi_d"
      },
      "source": [
        "### デフォルトの動作を設定してプロトコルを拡張する\n",
        "\n",
        "この例から、冗長性があることがわかります。電気自動車を充電するたびに、充電率のレベルを 100 に設定しなければなりません。ガソリン車の場合はガソリンタンクの容量によって異なりますが、すべての電気自動車の最大充電量は 100% であるため、電気自動車のデフォルトのレベルを 100 とすることができます。\n",
        "\n",
        "ここで役立つのが Swift の拡張機能です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NiHUJxXMtzSg"
      },
      "outputs": [],
      "source": [
        "extension Electric {\n",
        "    mutating func recharge() {\n",
        "        print(\"Recharging the battery...\")\n",
        "        batteryLevel = 100\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v8QbTb9NxWqI"
      },
      "source": [
        "こうすることで、以降で作成するすべての電気自動車は、充電する際にバッテリーを 100 に設定するようになりました。クラス構造、および列挙を、一意のデフォルトの動作でデコレートすることができました。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VIXakqxtvkp_"
      },
      "source": [
        "![Protocol Comic](https://koenig-media.raywenderlich.com/uploads/2015/06/protocols-extend.png)\n",
        "\n",
        "この挿絵を描いてくれた [Ray Wenderlich](https://www.raywenderlich.com/814-introducing-protocol-oriented-programming-in-swift-3) に感謝！"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MAo8n3zUR8Q9"
      },
      "source": [
        "しかし、気を付けなければならないことが 1 つあります。最初の実装では、`foo()` を `A` のデフォルトの実装として定義していますが、プロトコルでは必須になっていません。そのため、`a.foo()` を呼び出すと、「`A default`」が出力されてしまいます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "koP20_C9R7ps"
      },
      "outputs": [],
      "source": [
        "protocol Default {}\n",
        "\n",
        "extension Default {\n",
        "    func foo() { print(\"A default\")}\n",
        "}\n",
        "\n",
        "struct DefaultStruct: Default {\n",
        "    func foo() {\n",
        "        print(\"Inst\")\n",
        "    }\n",
        "}\n",
        "\n",
        "let a: Default = DefaultStruct()\n",
        "a.foo()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uSE5IWF_Sdet"
      },
      "source": [
        "しかし、`foo()` を `A` で必須にすると、「`Inst`」が出力されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DJ8jstIWSoUP"
      },
      "outputs": [],
      "source": [
        "protocol Default {\n",
        "    func foo()\n",
        "}\n",
        "\n",
        "extension Default {\n",
        "    func foo() { \n",
        "        print(\"A default\")\n",
        "    }\n",
        "}\n",
        "\n",
        "struct DefaultStruct: Default {\n",
        "    func foo() {\n",
        "        print(\"Inst\")\n",
        "    }\n",
        "}\n",
        "\n",
        "let a: Default = DefaultStruct()\n",
        "a.foo()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XC1juXPzZV8Q"
      },
      "source": [
        "これは、Swift のプロトコルにおける最初の例の静的ディスパッチと 2 つ目の性的ディスパッチが異なるためです。詳細については、こちらの[Medium の記事](https://medium.com/@PavloShadov/https-medium-com-pavloshadov-swift-protocols-magic-of-dynamic-static-methods-dispatches-dfe0e0c85509)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HzB0XC0wwMLD"
      },
      "source": [
        "### デフォルトの動作をオーバーライドする\n",
        "\n",
        "それでも、必要であれば、デフォルトの動作をオーバーライドすることができます。これは[動的ディスパッチはサポートしていない](https://stackoverflow.com/questions/44703205/swift-protocol-extension-method-is-called-instead-of-method-implemented-in-subcl)ことに注意してください。\n",
        "\n",
        "より古いバージョンの電気自動車があり、そのためバッテリーヘルスが 90% に減少してしまっていると仮定しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CaIZhcsVyBKz"
      },
      "outputs": [],
      "source": [
        "struct OldElectric: Car, Electric {\n",
        "    var color: Color // Needs to be a var since `Car` has a getter and setter.\n",
        "    let price: Int\n",
        "    var batteryLevel: Int\n",
        "    \n",
        "    func turnOn() {\n",
        "        print(\"Starting all systems!\")\n",
        "    }\n",
        "    \n",
        "    mutating func drive() {\n",
        "        print(\"Self driving engaged!\")\n",
        "        batteryLevel -= 8\n",
        "    }\n",
        "    \n",
        "    mutating func reCharge() {\n",
        "        print(\"Recharging the battery...\")\n",
        "        batteryLevel = 90\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c_Xmw5cDy_rZ"
      },
      "source": [
        "## プロトコルの標準ライブラリ使用\n",
        "\n",
        "Swift でどのようにプロトコルが機能するかを理解したので、標準ライブラリプロトコルを使用する典型的な例を見てみましょう。\n",
        "\n",
        "### 標準ライブラリを拡張する\n",
        "\n",
        "すでに Swift に存在する型に機能を追加する方法を確認します。Swift の型はビルトインではなく構造として標準ライブラリの一部であるため、機能の追加は簡単です。\n",
        "\n",
        "配列の要素に対して二分探索を行いながら、配列がソートされていることを確認しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-Jfn3P3P1RDt"
      },
      "outputs": [],
      "source": [
        "extension Collection where Element: Comparable {\n",
        "    // Verify that a `Collection` is sorted.\n",
        "    func isSorted(_ order: (Element, Element) -> Bool) -> Bool {\n",
        "        var i = index(startIndex, offsetBy: 1)\n",
        "        \n",
        "        while i < endIndex {\n",
        "            // The longer way of calling a binary function like `<(_:_:)`, \n",
        "            // `<=(_:_:)`, `==(_:_:)`, etc.\n",
        "            guard order(self[index(i, offsetBy: -1)], self[i]) else {\n",
        "                return false\n",
        "            }\n",
        "            i = index(after: i)\n",
        "        }\n",
        "        return true\n",
        "    }\n",
        "    \n",
        "    // Perform binary search on a `Collection`, verifying it is sorted.\n",
        "    func binarySearch(_ element: Element) -> Index? {\n",
        "        guard self.isSorted(<=) else {\n",
        "            return nil\n",
        "        }\n",
        "        \n",
        "        var low = startIndex\n",
        "        var high = endIndex\n",
        "        \n",
        "        while low <= high {\n",
        "            let mid = index(low, offsetBy: distance(from: low, to: high)/2)\n",
        "\n",
        "            if self[mid] == element {\n",
        "                return mid\n",
        "            } else if self[mid] < element {\n",
        "                low = index(after: mid)\n",
        "            } else {\n",
        "                high = index(mid, offsetBy: -1)\n",
        "            }\n",
        "        }\n",
        "        \n",
        "        return nil\n",
        "    }\n",
        "}\n",
        "\n",
        "print([2, 2, 5, 7, 11, 13, 17].binarySearch(5)!)\n",
        "print([\"a\", \"b\", \"c\", \"d\"].binarySearch(\"b\")!)\n",
        "print([1.1, 2.2, 3.3, 4.4, 5.5].binarySearch(3.3)!)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CIh7qyFVqlaH"
      },
      "source": [
        "これは、*「要素を非破壊的に何度もトラバースし、インデックス付きのサブスクリプトによってアクセスされるシーケンス」*を定義する [`Collection`](https://developer.apple.com/documentation/swift/collection) プロトコルを拡張して行います。配列は角括弧表記を使ってインデックス化できるため、これが拡張するプロトコルです。\n",
        "\n",
        "同様に、比較可能な要素を持つ配列にのみ、このユーティリティ関数を追加します。`where Element: Comparable` はこのために存在します。\n",
        "\n",
        "`where` 句は、Swift の型体系の一部です。これは後で説明しますが、手短に述べると、記述している拡張機能に、型にプロトコルを実装しなければならないという要件、2 つの型は同一でなければならないという要件、またはクラスに特定のスーパークラスが必要であるという要件などを追加することができます。\n",
        "\n",
        "[`Element`](https://developer.apple.com/documentation/swift/sequence/2908099-element) は、`Collection` 準拠型の要素の関連型です。`Element` は、[`Sequence`](https://developer.apple.com/documentation/swift/sequence) プロトコル内に定義されていますが、`Collection` は `Sequence` から継承するため、`Element` 関連型を継承します。\n",
        "\n",
        "[`Comparable`](https://developer.apple.com/documentation/swift/comparable) は、*「リレーショナル演算子 `<`、`<=`、`>=`、および `>` を使って比較できる型」*を定義するプロトコルです。ソート済みの `Collection` に対して二分探索を実行しているため、これはもちろん true である必要があります。true でない場合は、二分探索で左から右に再帰/イテレートするのかがわかりません。\n",
        "\n",
        "実装の注意書きとして、使用された `index(_:offsetBy:)` 関数についての詳細は、次の[ドキュメント](https://developer.apple.com/documentation/swift/string/1786175-index)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TmqFx2layKs7"
      },
      "source": [
        "## ジェネリック + プロトコル = 💥\n",
        "\n",
        "ジェネリックとプロトコルは、コードを重複させないように正しく使用されれば強力なツールと言えます。\n",
        "\n",
        "まず、[Swift ツアー](https://colab.research.google.com/github/tensorflow/swift/blob/master/docs/site/tutorials/a_swift_tour.ipynb)という別のチュートリアルをご覧ください。Colab ブックの最後に、ジェネリックについて簡単に説明されています。\n",
        "\n",
        "ジェネリックがどのようなものか大まかに理解していると仮定し、少し高度な使用方法をさっと見てみましょう。\n",
        "\n",
        "単一の型に、複数のプロトコルに適合する型といった要件が複数ある場合、いくつかのオプションを使用できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "HIjkHLGtz268"
      },
      "outputs": [],
      "source": [
        "typealias ComparableReal = Comparable & FloatingPoint\n",
        "\n",
        "func foo1<T: ComparableReal>(a: T, b: T) -> Bool {\n",
        "    return a > b\n",
        "}\n",
        "\n",
        "func foo2<T: Comparable & FloatingPoint>(a: T, b: T) -> Bool {\n",
        "    return a > b\n",
        "}\n",
        "\n",
        "func foo3<T>(a: T, b: T) -> Bool where T: ComparableReal {\n",
        "    return a > b\n",
        "}\n",
        "\n",
        "func foo4<T>(a: T, b: T) -> Bool where T: Comparable & FloatingPoint {\n",
        "    return a > b\n",
        "}\n",
        "\n",
        "func foo5<T: FloatingPoint>(a: T, b: T) -> Bool where T: Comparable {\n",
        "    return a > b\n",
        "}\n",
        "\n",
        "print(foo1(a: 1, b: 2))\n",
        "print(foo2(a: 1, b: 2))\n",
        "print(foo3(a: 1, b: 2))\n",
        "print(foo4(a: 1, b: 2))\n",
        "print(foo5(a: 1, b: 2))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DMCioS9Dz5Fh"
      },
      "source": [
        "一番上で `typealias` が使用されいるところに注目してください。これは、既存の型の名前付きエイリアスをプログラムに追加します。型エイリアスが宣言された後は、既存の型の代わりにエイリアス名をプログラム中で使用できるようになります。型エイリアスは、新しい型を作成するのではなく、既存の型を名前で参照できるようにするだけです。\n",
        "\n",
        "次に、プロトコルとジェネリックを合わせて使用する方法を見てみましょう。\n",
        "\n",
        "コンピュータストアを運営しており、販売するノートブックについて、在庫をどのように整理するかを決定する上で、次の要件があるとします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oKvieIsw2YJW"
      },
      "outputs": [],
      "source": [
        "enum Box {\n",
        "    case small\n",
        "    case medium\n",
        "    case large\n",
        "}\n",
        "\n",
        "enum Mass {\n",
        "    case light\n",
        "    case medium\n",
        "    case heavy\n",
        "}\n",
        "\n",
        "// Note: `CustomStringConvertible` protocol lets us pretty-print a `Laptop`.\n",
        "struct Laptop: CustomStringConvertible {\n",
        "    var name: String\n",
        "    var box: Box\n",
        "    var mass: Mass\n",
        "    \n",
        "    var description: String {\n",
        "        return \"(\\(self.name) \\(self.box) \\(self.mass))\"\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ey519vu04FgG"
      },
      "source": [
        "しかし、棚には重量制限があるため、`Laptop` を質量別にグループ分けするという新しい要件が加わりました。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tVRuqx_q4jQ9"
      },
      "outputs": [],
      "source": [
        "func filtering(_ laptops: [Laptop], by mass: Mass) -> [Laptop] {\n",
        "    return laptops.filter { $0.mass == mass }\n",
        "}\n",
        "\n",
        "let laptops: [Laptop] = [\n",
        "    Laptop(name: \"a\", box: .small, mass: .light),\n",
        "    Laptop(name: \"b\", box: .large, mass: .medium),\n",
        "    Laptop(name: \"c\", box: .medium, mass: .heavy),\n",
        "    Laptop(name: \"d\", box: .large, mass: .light)\n",
        "]\n",
        "\n",
        "let filteredLaptops = filtering(laptops, by: .light)\n",
        "print(filteredLaptops)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Lw_W5zW17UMc"
      },
      "source": [
        "さらに、`Mass` 以外の要素で絞り込む場合はどうすればよいのでしょうか。\n",
        "\n",
        "1 つのオプションとして、次のように行うことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2qDVD9Yl8POQ"
      },
      "outputs": [],
      "source": [
        "// Define a protocol which will act as our comparator.\n",
        "protocol DeviceFilterPredicate {\n",
        "    associatedtype Device\n",
        "    func shouldKeep(_ item: Device) -> Bool\n",
        "}\n",
        "\n",
        "// Define the structs we will use for passing into our filtering function.\n",
        "struct BoxFilter: DeviceFilterPredicate {\n",
        "    typealias Device = Laptop\n",
        "    var box: Box \n",
        "    \n",
        "    func shouldKeep(_ item: Laptop) -> Bool {\n",
        "        return item.box == box\n",
        "    }\n",
        "}\n",
        "\n",
        "struct MassFilter: DeviceFilterPredicate {\n",
        "    typealias Device = Laptop  \n",
        "    var mass: Mass\n",
        "    \n",
        "    func shouldKeep(_ item: Laptop) -> Bool {\n",
        "        return item.mass == mass\n",
        "    }\n",
        "}\n",
        "\n",
        "// Make sure our filter conforms to `DeviceFilterPredicate` and that we are \n",
        "// filtering `Laptop`s.\n",
        "func filtering<F: DeviceFilterPredicate>(\n",
        "    _ laptops: [Laptop], \n",
        "    by filter: F\n",
        ") -> [Laptop] where Laptop == F.Device {\n",
        "    return laptops.filter { filter.shouldKeep($0) }\n",
        "}\n",
        "\n",
        "// Let's test the function out!\n",
        "print(filtering(laptops, by: BoxFilter(box: .large)))\n",
        "print(filtering(laptops, by: MassFilter(mass: .heavy)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yFx_6y0-CRHc"
      },
      "source": [
        "素晴らしい！これで、ノートブックの任意の制約に基づいてフィルタできるようになりました。ただし、フィルタできるのは `Laptop` のみです。\n",
        "\n",
        "箱に含まれており質量のあるものでフィルタリングするようにできないでしょうか。このノートブックの倉庫は、さまざまな顧客ベースのあるサーバーにも使用されているかもしれません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EzhE_K-eCvYt"
      },
      "outputs": [],
      "source": [
        "// Define 2 new protocols so we can filter anything in a box and which has mass.\n",
        "protocol Weighable {\n",
        "    var mass: Mass { get }\n",
        "}\n",
        "\n",
        "protocol Boxed {\n",
        "    var box: Box { get }\n",
        "}\n",
        "\n",
        "// Define the new Laptop and Server struct which have mass and a box.\n",
        "struct Laptop: CustomStringConvertible, Boxed, Weighable {\n",
        "    var name: String\n",
        "    var box: Box\n",
        "    var mass: Mass\n",
        "    \n",
        "    var description: String {\n",
        "        return \"(\\(self.name) \\(self.box) \\(self.mass))\"\n",
        "    }\n",
        "}\n",
        "\n",
        "struct Server: CustomStringConvertible, Boxed, Weighable {\n",
        "    var isWorking: Bool\n",
        "    var name: String\n",
        "    let box: Box\n",
        "    let mass: Mass\n",
        "\n",
        "    var description: String {\n",
        "        if isWorking {\n",
        "            return \"(working \\(self.name) \\(self.box) \\(self.mass))\"\n",
        "        } else {\n",
        "            return \"(notWorking \\(self.name) \\(self.box) \\(self.mass))\"\n",
        "        }\n",
        "    }\n",
        "}\n",
        "\n",
        "// Define the structs we will use for passing into our filtering function.\n",
        "struct BoxFilter<T: Boxed>: DeviceFilterPredicate {\n",
        "    var box: Box \n",
        "  \n",
        "    func shouldKeep(_ item: T) -> Bool {\n",
        "        return item.box == box\n",
        "    }\n",
        "}\n",
        "\n",
        "struct MassFilter<T: Weighable>: DeviceFilterPredicate {\n",
        "    var mass: Mass\n",
        "    \n",
        "    func shouldKeep(_ item: T) -> Bool {\n",
        "        return item.mass == mass\n",
        "    }\n",
        "}\n",
        "\n",
        "// Define the new filter function.\n",
        "func filtering<F: DeviceFilterPredicate, T>(\n",
        "    _ elements: [T], \n",
        "    by filter: F\n",
        ") -> [T] where T == F.Device {\n",
        "    return elements.filter { filter.shouldKeep($0) }\n",
        "}\n",
        "\n",
        "\n",
        "// Let's test the function out!\n",
        "let servers = [\n",
        "    Server(isWorking: true, name: \"serverA\", box: .small, mass: .heavy),\n",
        "    Server(isWorking: false, name: \"serverB\", box: .medium, mass: .medium),\n",
        "    Server(isWorking: true, name: \"serverC\", box: .large, mass: .light),\n",
        "    Server(isWorking: false, name: \"serverD\", box: .medium, mass: .light),\n",
        "    Server(isWorking: true, name: \"serverE\", box: .small, mass: .heavy)\n",
        "]\n",
        "\n",
        "let products = [\n",
        "    Laptop(name: \"a\", box: .small, mass: .light),\n",
        "    Laptop(name: \"b\", box: .large, mass: .medium),\n",
        "    Laptop(name: \"c\", box: .medium, mass: .heavy),\n",
        "    Laptop(name: \"d\", box: .large, mass: .light)\n",
        "]\n",
        "\n",
        "print(filtering(servers, by: BoxFilter(box: .small)))\n",
        "print(filtering(servers, by: MassFilter(mass: .medium)))\n",
        "\n",
        "print(filtering(products, by: BoxFilter(box: .small)))\n",
        "print(filtering(products, by: MassFilter(mass: .medium)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-UrRDSaFNCRg"
      },
      "source": [
        "これで、特定の `struct` のすべてのプロパティでだけでなく、そのプロパティを持つすべての構造体でもフィルタできるようになりました。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6g3pPZIuMvPu"
      },
      "source": [
        "# 最適な API 設計を得るためのヒント\n",
        "\n",
        "***このセクションは、[WWDC 2019: Modern Swift API Design](https://developer.apple.com/videos/play/wwdc2019/415/) の講演から抜粋したものです。***\n",
        "\n",
        "プロトコルの振る舞いを理解したので、プロトコルを使用するタイミングについて説明することにします。プロトコルが強力だからといって、すぐにプロトコルを使用し始めるのは必ずしも最善策というわけではありません。\n",
        "\n",
        "- 具体的なユースケースから始めましょう。\n",
        "    - まずは具体的な型が伴うユースケースを調査し、共有して検索するどのコードが繰り返されているのかを把握します。次に、その共有コードをジェネリックで抜き取ります。新しいプロトコルを作成した方が良いのかもしれません。ジェネリックコードの必要性を探索してみましょう。\n",
        "- 標準ライブラリに定義されている既存のプロトコルから新しいプロトコルを作成することを検討しましょう。この良い例につちえは、次の [Apple ドキュメント](https://developer.apple.com/documentation/swift/adopting_common_protocols)をご覧ください。\n",
        "- ジェネリックプロトコルの代わりに、ジェネリック型を定義することを検討しましょう。\n",
        "\n",
        "## 例: カスタムベクトル型を定義する\n",
        "\n",
        "3 つの重要なベクトル演算を定義する、作成中の幾何学アプリで使用するために、浮動小数点数で `GeometricVector` プロトコルを定義するとします。\n",
        "\n",
        "```swift\n",
        "protocol GeometricVector {\n",
        "    associatedtype Scalar: FloatingPoint\n",
        "    static func dot(_ a: Self, _ b: Self) -&gt; Scalar\n",
        "    var length: Scalar { get }\n",
        "    func distance(to other: Self) -&gt; Scalar\n",
        "}\n",
        "```\n",
        "\n",
        "ベクトルの次元を保存するとします。これには `SIMD` プロトコルを利用できるため、新しい型で `SIMD` プロトコルを改善することにします。`SIMD` ベクトルは、ベクトル演算を実行するために使用する場合に非常に高速な、固定サイズのベクトルとして捉えることができます。\n",
        "\n",
        "```swift\n",
        "protocol GeometricVector: SIMD {\n",
        "    associatedtype Scalar: FloatingPoint\n",
        "    static func dot(_ a: Self, _ b: Self) -&gt; Scalar\n",
        "    var length: Scalar { get }\n",
        "    func distance(to other: Self) -&gt; Scalar\n",
        "}\n",
        "```\n",
        "\n",
        "次に、上記の演算のデフォルトの実装を定義しましょう。\n",
        "\n",
        "```swift\n",
        "extension GeometricVector {\n",
        "    static func dot(_ a: Self, _ b: Self) -&gt; Scalar {\n",
        "        (a * b).sum()\n",
        "    }\n",
        "\n",
        "    var length: Scalar {\n",
        "        Self.dot(self, self).squareRoot()\n",
        "    }\n",
        "\n",
        "    func distance(to other: Self) -&gt; Scalar {\n",
        "        (self - other).length\n",
        "    }\n",
        "}\n",
        "```\n",
        "\n",
        "そして、これらの機能を追加する型に適合性を追加する必要があります。\n",
        "\n",
        "```swift\n",
        "extension SIMD2: GeometricVector where Scalar: FloatingPoint { } extension SIMD3: GeometricVector where Scalar: FloatingPoint { } extension SIMD4: GeometricVector where Scalar: FloatingPoint { } extension SIMD8: GeometricVector where Scalar: FloatingPoint { } extension SIMD16: GeometricVector where Scalar: FloatingPoint { } extension SIMD32: GeometricVector where Scalar: FloatingPoint { } extension SIMD64: GeometricVector where Scalar: FloatingPoint { }\n",
        "```\n",
        "\n",
        "デフォルトの実装を提供してから複数の型に適合性を追加するという、上記の 3 つのステップで行うプロトコルの定義プロセスは非常に多く繰り返されます。\n",
        "\n",
        "## プロトコルは必要でしたか？\n",
        "\n",
        "一意の実装を持つ `SIMD` 型がないというのは、警告サインとして捉えることができます。このケースでは、プロトコルは特に役に立っていないということです。\n",
        "\n",
        "## `SIMD` の拡張機能で定義する\n",
        "\n",
        "`SIMD` プロトコルの拡張機能に 3 つの演算子を記述すれば、問題をより簡潔に解決できます。\n",
        "\n",
        "```swift\n",
        "extension SIMD where Scalar: FloatingPoint {\n",
        "    static func dot(_ a: Self, _ b: Self) -&gt; Scalar {\n",
        "        (a * b).sum()\n",
        "    }\n",
        "\n",
        "    var length: Scalar {\n",
        "        Self.dot(self, self).squareRoot()\n",
        "    }\n",
        "\n",
        "    func distance(to other: Self) -&gt; Scalar {\n",
        "        (self - other).length\n",
        "    }\n",
        "}\n",
        "```\n",
        "\n",
        "コードの行数を減らし、`SIMD` のすべての型にデフォルト実装をすべて追加しました。\n",
        "\n",
        "この型の階層を作成したくなることがあるかもしれませんが、必ずしも必要ではないことに留意してください。また、コンパイル済みのプログラムのバイナリサイズが縮小され、コンパイルの加速化にもつながるでしょう。\n",
        "\n",
        "ただし、この拡張機能のアプローチは、追加するメソッドがいくつかある場合に最適ではありますが、より大規模な API を設計する場合にはスケーラビリティの問題が発生してしまいます。\n",
        "\n",
        "## Is-a と Has-a\n",
        "\n",
        "前の方で、`GeometricVector` は `SIMD` を改善すると述べました。しかし、これはIs-a 関係でしょうか。問題は、`SIMD` がスカラー 1 とベクトルに追加できる演算を定義しますが、幾何学のコンテキストでそのような演算を定義する意味がないということです。\n",
        "\n",
        "そのため、`SIMD` を、あらゆる浮動小数点数を処理できる新しいジェネリック型でラップする Has-a 関係の方が適しているかもしれません。\n",
        "\n",
        "```swift\n",
        "// NOTE: `Storage` is the underlying type that is storing the values,\n",
        "// just like in a `SIMD` vector.\n",
        "struct GeometricVector<storage: simd> where Storage.Scalar: FloatingPoint {\n",
        "    typealias Scalar = Storage.Scalar\n",
        "    var value: Storage\n",
        "    init(_ value: Storage) { self.value = value }\n",
        "}</storage:>\n",
        "```\n",
        "\n",
        "そして、慎重に、幾何学のコンテキストでのみ意味のある演算のみを定義することができます。\n",
        "\n",
        "```swift\n",
        "extension GeometricVector {\n",
        "    static func + (a: Self, b: Self) -&gt; Self {\n",
        "        Self(a.value + b.value)\n",
        "    }\n",
        "\n",
        "    static func - (a: Self, b: Self) -&gt; Self {\n",
        "        Self(a.value - b.value)\n",
        "    }\n",
        "    static func * (a: Self, b: Scalar) -&gt; Self {\n",
        "        Self(a.value * b)\n",
        "    }\n",
        "}\n",
        "```\n",
        "\n",
        "それでも、ジェネリック拡張機能を使用して、前に実装しようとしていた 3 つの演算子を取得することができます。これは、前とほぼ全く同じです。\n",
        "\n",
        "```swift\n",
        "extension GeometricVector {\n",
        "    static func dot(_ a: Self, _ b: Self) -&gt; Scalar {\n",
        "        (a.value * b.value).sum()\n",
        "    }\n",
        "\n",
        "    var length: Scalar {\n",
        "        Self.dot(self, self).squareRoot()\n",
        "    }\n",
        "\n",
        "    func distance(to other: Self) -&gt; Scalar {\n",
        "        (self - other).length\n",
        "    }\n",
        "}\n",
        "```\n",
        "\n",
        "全体として振り返ると、単に構造を使用することで、3 つの演算の動作を 1 つの型に精緻化することができました。プロトコルでは、反復的な適合性をすべての `SIMD` ベクトルに記述しなければならない問題に遭遇し、`Scalar + Vector` といった特定の演算子を使用できないようにすることはできませんでした（この場合では、使用できないようにしませんでした）。したがって、プロトコルは究極のソリューションではなく、より従来的なソリューションがより強力であることもあることに留意しましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UVLumcugImNx"
      },
      "source": [
        "# プロトコル指向プログラミングに関するその他のリソース\n",
        "\n",
        "ここで取り扱ったトピックに関するその他のリソースを以下に示します。\n",
        "\n",
        "- [WWDC 2015: Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/play/wwdc2015/408/): これは Swift 2 を使って紹介されているため、多くの内容が現バージョンとは異なりますが（プレゼンテーションで使用されているプロトコル名など）、理論とそれに基づく使用方法を理解する上で役立つリソースです。\n",
        "- [Introducing Protocol-Oriented Programming in Swift 3](https://www.raywenderlich.com/814-introducing-protocol-oriented-programming-in-swift-3): これは Swift 3 で書かれているため、正しくコンパイルするにはコードを変更しなければならい箇所があるかもしれませんが、素晴らしいリソースです。\n",
        "- [WWDC 2019: Modern Swift API Design](https://developer.apple.com/videos/play/wwdc2019/415/): 値と参照型の違い、API 設計においてプロトコルが悪い選択肢であることを実証するユースケース（上記の「最適な API 設計を得るためのヒント」と同じことを言及しています）、キーパスのメンバールックアップ、、およびプロパティラッパーについて説明されています。\n",
        "- [Generics](https://docs.swift.org/swift-book/LanguageGuide/Generics.html): Swift 5 のジェネリックについて説明された Swift のドキュメントです。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "protocol_oriented_generics.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Swift",
      "name": "swift"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
